#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
智能代码片段管理器
提供常用SQL代码片段的管理和插入功能
"""

import json
import logging
import re
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass, asdict
from pathlib import Path

from PySide6.QtCore import Qt, Signal, QObject, QTimer
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLineEdit,
                              QPushButton, QListWidget, QListWidgetItem,
                              QTextEdit, QLabel, QComboBox, QDialog,
                              QDialogButtonBox, QFormLayout, QMessageBox,
                              QSplitter, QGroupBox, QCheckBox)
from PySide6.QtGui import QFont, QIcon, QKeySequence, QShortcut

from config.settings import settings

logger = logging.getLogger("snippet_manager")


@dataclass
class CodeSnippet:
    """代码片段数据类"""
    name: str
    description: str
    content: str
    category: str = "通用"
    keywords: List[str] = None
    variables: List[str] = None  # 可替换变量
    shortcut: str = ""
    usage_count: int = 0
    
    def __post_init__(self):
        if self.keywords is None:
            self.keywords = []
        if self.variables is None:
            self.variables = []


class SnippetStorage:
    """代码片段存储管理"""
    
    def __init__(self):
        """初始化存储管理器"""
        self.config_dir = Path.home() / ".hq-reader" / "snippets"
        self.config_dir.mkdir(parents=True, exist_ok=True)
        self.snippets_file = self.config_dir / "snippets.json"
        self.snippets = {}
        self._load_snippets()
        
    def _load_snippets(self):
        """加载代码片段"""
        try:
            if self.snippets_file.exists():
                with open(self.snippets_file, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    self.snippets = {
                        name: CodeSnippet(**snippet_data)
                        for name, snippet_data in data.items()
                    }
            else:
                # 创建默认片段
                self._create_default_snippets()
                
        except Exception as e:
            logger.error(f"加载代码片段失败: {str(e)}")
            self._create_default_snippets()
            
    def _create_default_snippets(self):
        """创建默认代码片段"""
        default_snippets = [
            CodeSnippet(
                name="基本查询",
                description="基本的SELECT查询模板",
                content="SELECT ${1:columns}\nFROM ${2:table_name}\nWHERE ${3:condition};",
                category="查询",
                keywords=["select", "query", "基本"],
                variables=["columns", "table_name", "condition"],
                shortcut="sel"
            ),
            CodeSnippet(
                name="连接查询",
                description="内连接查询模板",
                content="SELECT ${1:t1.column1}, ${2:t2.column2}\nFROM ${3:table1} t1\nINNER JOIN ${4:table2} t2 ON t1.${5:id} = t2.${6:foreign_id}\nWHERE ${7:condition};",
                category="查询",
                keywords=["join", "inner", "连接"],
                variables=["t1.column1", "t2.column2", "table1", "table2", "id", "foreign_id", "condition"],
                shortcut="join"
            ),
            CodeSnippet(
                name="分组统计",
                description="GROUP BY统计查询",
                content="SELECT ${1:column}, COUNT(*) as count\nFROM ${2:table_name}\nGROUP BY ${1:column}\nHAVING COUNT(*) > ${3:1}\nORDER BY count DESC;",
                category="统计",
                keywords=["group", "count", "统计"],
                variables=["column", "table_name", "1"],
                shortcut="grp"
            ),
            CodeSnippet(
                name="创建表",
                description="创建表的模板",
                content="CREATE TABLE ${1:table_name} (\n    ${2:id} INTEGER PRIMARY KEY,\n    ${3:column_name} ${4:data_type},\n    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);",
                category="DDL",
                keywords=["create", "table", "创建"],
                variables=["table_name", "id", "column_name", "data_type"],
                shortcut="ct"
            ),
            CodeSnippet(
                name="插入数据",
                description="插入数据的模板",
                content="INSERT INTO ${1:table_name} (${2:column1}, ${3:column2})\nVALUES (${4:value1}, ${5:value2});",
                category="DML",
                keywords=["insert", "插入"],
                variables=["table_name", "column1", "column2", "value1", "value2"],
                shortcut="ins"
            ),
            CodeSnippet(
                name="更新数据",
                description="更新数据的模板",
                content="UPDATE ${1:table_name}\nSET ${2:column} = ${3:value}\nWHERE ${4:condition};",
                category="DML",
                keywords=["update", "更新"],
                variables=["table_name", "column", "value", "condition"],
                shortcut="upd"
            ),
            CodeSnippet(
                name="窗口函数",
                description="窗口函数查询模板",
                content="SELECT ${1:column},\n       ROW_NUMBER() OVER (PARTITION BY ${2:partition_column} ORDER BY ${3:order_column}) as row_num\nFROM ${4:table_name};",
                category="高级查询",
                keywords=["window", "row_number", "窗口"],
                variables=["column", "partition_column", "order_column", "table_name"],
                shortcut="win"
            ),
            CodeSnippet(
                name="CTE查询",
                description="公用表表达式查询",
                content="WITH ${1:cte_name} AS (\n    SELECT ${2:columns}\n    FROM ${3:table_name}\n    WHERE ${4:condition}\n)\nSELECT *\nFROM ${1:cte_name};",
                category="高级查询",
                keywords=["with", "cte", "公用表"],
                variables=["cte_name", "columns", "table_name", "condition"],
                shortcut="with"
            ),
            CodeSnippet(
                name="存在性检查",
                description="检查记录是否存在",
                content="SELECT CASE \n    WHEN EXISTS (SELECT 1 FROM ${1:table_name} WHERE ${2:condition}) \n    THEN '存在' \n    ELSE '不存在' \nEND as result;",
                category="查询",
                keywords=["exists", "存在", "检查"],
                variables=["table_name", "condition"],
                shortcut="ex"
            ),
            CodeSnippet(
                name="分页查询",
                description="分页查询模板",
                content="SELECT *\nFROM ${1:table_name}\nORDER BY ${2:order_column}\nLIMIT ${3:page_size} OFFSET ${4:offset};",
                category="查询",
                keywords=["limit", "offset", "分页"],
                variables=["table_name", "order_column", "page_size", "offset"],
                shortcut="page"
            )
        ]
        
        for snippet in default_snippets:
            self.snippets[snippet.name] = snippet
            
        self.save_snippets()
        
    def save_snippets(self):
        """保存代码片段"""
        try:
            data = {
                name: asdict(snippet)
                for name, snippet in self.snippets.items()
            }
            with open(self.snippets_file, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=2)
                
        except Exception as e:
            logger.error(f"保存代码片段失败: {str(e)}")
            
    def add_snippet(self, snippet: CodeSnippet) -> bool:
        """添加代码片段"""
        try:
            self.snippets[snippet.name] = snippet
            self.save_snippets()
            return True
        except Exception as e:
            logger.error(f"添加代码片段失败: {str(e)}")
            return False
            
    def remove_snippet(self, name: str) -> bool:
        """删除代码片段"""
        try:
            if name in self.snippets:
                del self.snippets[name]
                self.save_snippets()
                return True
            return False
        except Exception as e:
            logger.error(f"删除代码片段失败: {str(e)}")
            return False
            
    def get_snippet(self, name: str) -> Optional[CodeSnippet]:
        """获取代码片段"""
        return self.snippets.get(name)
        
    def get_all_snippets(self) -> Dict[str, CodeSnippet]:
        """获取所有代码片段"""
        return self.snippets.copy()
        
    def search_snippets(self, query: str) -> List[CodeSnippet]:
        """搜索代码片段"""
        query = query.lower()
        results = []
        
        for snippet in self.snippets.values():
            # 搜索名称、描述、关键字
            if (query in snippet.name.lower() or
                query in snippet.description.lower() or
                any(query in keyword.lower() for keyword in snippet.keywords) or
                query == snippet.shortcut.lower()):
                results.append(snippet)
                
        # 按使用次数排序
        results.sort(key=lambda x: x.usage_count, reverse=True)
        return results
        
    def increment_usage(self, name: str):
        """增加使用次数"""
        if name in self.snippets:
            self.snippets[name].usage_count += 1
            self.save_snippets()


class SnippetEditor(QDialog):
    """代码片段编辑器"""
    
    def __init__(self, snippet: Optional[CodeSnippet] = None, parent=None):
        """初始化片段编辑器
        
        Args:
            snippet: 要编辑的片段，None表示新建
            parent: 父窗口
        """
        super().__init__(parent)
        self.snippet = snippet
        self.setWindowTitle("编辑代码片段" if snippet else "新建代码片段")
        self.setModal(True)
        self.resize(600, 500)
        self._init_ui()
        
        if snippet:
            self._load_snippet_data()
            
    def _init_ui(self):
        """初始化用户界面"""
        layout = QVBoxLayout(self)
        
        # 表单布局
        form_layout = QFormLayout()
        
        # 名称
        self.name_edit = QLineEdit()
        self.name_edit.setPlaceholderText("片段名称")
        form_layout.addRow("名称:", self.name_edit)
        
        # 描述
        self.description_edit = QLineEdit()
        self.description_edit.setPlaceholderText("片段描述")
        form_layout.addRow("描述:", self.description_edit)
        
        # 分类
        self.category_combo = QComboBox()
        self.category_combo.setEditable(True)
        self.category_combo.addItems(["通用", "查询", "DML", "DDL", "统计", "高级查询"])
        form_layout.addRow("分类:", self.category_combo)
        
        # 快捷键
        self.shortcut_edit = QLineEdit()
        self.shortcut_edit.setPlaceholderText("快捷键（可选）")
        form_layout.addRow("快捷键:", self.shortcut_edit)
        
        # 关键字
        self.keywords_edit = QLineEdit()
        self.keywords_edit.setPlaceholderText("关键字，用逗号分隔")
        form_layout.addRow("关键字:", self.keywords_edit)
        
        layout.addLayout(form_layout)
        
        # 内容编辑
        content_group = QGroupBox("代码内容")
        content_layout = QVBoxLayout(content_group)
        
        # 说明标签
        help_label = QLabel("使用 ${数字:默认值} 格式定义可替换变量，例如: ${1:table_name}")
        help_label.setStyleSheet("color: #666; font-size: 11px;")
        content_layout.addWidget(help_label)
        
        self.content_edit = QTextEdit()
        self.content_edit.setFont(QFont("Consolas", 10))
        self.content_edit.setPlaceholderText("输入SQL代码片段...")
        content_layout.addWidget(self.content_edit)
        
        layout.addWidget(content_group)
        
        # 预览
        preview_group = QGroupBox("预览")
        preview_layout = QVBoxLayout(preview_group)
        
        self.preview_edit = QTextEdit()
        self.preview_edit.setFont(QFont("Consolas", 10))
        self.preview_edit.setReadOnly(True)
        self.preview_edit.setMaximumHeight(100)
        preview_layout.addWidget(self.preview_edit)
        
        layout.addWidget(preview_group)
        
        # 按钮
        button_box = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel
        )
        button_box.accepted.connect(self.accept)
        button_box.rejected.connect(self.reject)
        layout.addWidget(button_box)
        
        # 连接信号
        self.content_edit.textChanged.connect(self._update_preview)
        
    def _load_snippet_data(self):
        """加载片段数据"""
        if not self.snippet:
            return
            
        self.name_edit.setText(self.snippet.name)
        self.description_edit.setText(self.snippet.description)
        self.category_combo.setCurrentText(self.snippet.category)
        self.shortcut_edit.setText(self.snippet.shortcut)
        self.keywords_edit.setText(", ".join(self.snippet.keywords))
        self.content_edit.setPlainText(self.snippet.content)
        
    def _update_preview(self):
        """更新预览"""
        content = self.content_edit.toPlainText()
        # 简单的变量替换预览
        preview = re.sub(r'\$\{(\d+):([^}]+)\}', r'\2', content)
        self.preview_edit.setPlainText(preview)
        
    def get_snippet(self) -> Optional[CodeSnippet]:
        """获取编辑后的片段"""
        name = self.name_edit.text().strip()
        if not name:
            QMessageBox.warning(self, "错误", "请输入片段名称")
            return None
            
        content = self.content_edit.toPlainText().strip()
        if not content:
            QMessageBox.warning(self, "错误", "请输入代码内容")
            return None
            
        # 提取变量
        variables = re.findall(r'\$\{(\d+):([^}]+)\}', content)
        variable_names = [var[1] for var in variables]
        
        keywords = [k.strip() for k in self.keywords_edit.text().split(",") if k.strip()]
        
        return CodeSnippet(
            name=name,
            description=self.description_edit.text().strip(),
            content=content,
            category=self.category_combo.currentText(),
            keywords=keywords,
            variables=variable_names,
            shortcut=self.shortcut_edit.text().strip(),
            usage_count=self.snippet.usage_count if self.snippet else 0
        )


class SnippetManager(QWidget):
    """代码片段管理器主界面"""
    
    snippet_selected = Signal(str)  # 选中片段信号
    
    def __init__(self, parent=None):
        """初始化片段管理器"""
        super().__init__(parent)
        self.storage = SnippetStorage()
        self._init_ui()
        self._load_snippets()
        
    def _init_ui(self):
        """初始化用户界面"""
        layout = QVBoxLayout(self)
        
        # 工具栏
        toolbar_layout = QHBoxLayout()
        
        # 搜索框
        self.search_edit = QLineEdit()
        self.search_edit.setPlaceholderText("搜索代码片段...")
        self.search_edit.textChanged.connect(self._filter_snippets)
        toolbar_layout.addWidget(self.search_edit)
        
        # 分类过滤
        self.category_filter = QComboBox()
        self.category_filter.addItem("所有分类")
        self.category_filter.currentTextChanged.connect(self._filter_snippets)
        toolbar_layout.addWidget(self.category_filter)
        
        # 按钮
        self.add_btn = QPushButton("新建")
        self.add_btn.clicked.connect(self._add_snippet)
        toolbar_layout.addWidget(self.add_btn)
        
        self.edit_btn = QPushButton("编辑")
        self.edit_btn.clicked.connect(self._edit_snippet)
        self.edit_btn.setEnabled(False)
        toolbar_layout.addWidget(self.edit_btn)
        
        self.delete_btn = QPushButton("删除")
        self.delete_btn.clicked.connect(self._delete_snippet)
        self.delete_btn.setEnabled(False)
        toolbar_layout.addWidget(self.delete_btn)
        
        layout.addLayout(toolbar_layout)
        
        # 分割器
        splitter = QSplitter(Qt.Horizontal)
        
        # 片段列表
        self.snippet_list = QListWidget()
        self.snippet_list.itemSelectionChanged.connect(self._on_selection_changed)
        self.snippet_list.itemDoubleClicked.connect(self._on_item_double_clicked)
        splitter.addWidget(self.snippet_list)
        
        # 预览区域
        preview_widget = QWidget()
        preview_layout = QVBoxLayout(preview_widget)
        
        # 片段信息
        self.info_label = QLabel("选择一个代码片段查看详情")
        self.info_label.setWordWrap(True)
        preview_layout.addWidget(self.info_label)
        
        # 代码预览
        self.preview_text = QTextEdit()
        self.preview_text.setFont(QFont("Consolas", 10))
        self.preview_text.setReadOnly(True)
        preview_layout.addWidget(self.preview_text)
        
        # 插入按钮
        self.insert_btn = QPushButton("插入代码片段")
        self.insert_btn.clicked.connect(self._insert_snippet)
        self.insert_btn.setEnabled(False)
        preview_layout.addWidget(self.insert_btn)
        
        splitter.addWidget(preview_widget)
        splitter.setSizes([300, 400])
        
        layout.addWidget(splitter)
        
    def _load_snippets(self):
        """加载代码片段"""
        self.snippet_list.clear()
        
        # 更新分类过滤器
        categories = set()
        for snippet in self.storage.get_all_snippets().values():
            categories.add(snippet.category)
            
        self.category_filter.clear()
        self.category_filter.addItem("所有分类")
        self.category_filter.addItems(sorted(categories))
        
        # 加载片段到列表
        self._filter_snippets()
        
    def _filter_snippets(self):
        """过滤代码片段"""
        search_text = self.search_edit.text().strip()
        category_filter = self.category_filter.currentText()
        
        self.snippet_list.clear()
        
        snippets = self.storage.get_all_snippets().values()
        
        # 应用搜索过滤
        if search_text:
            snippets = self.storage.search_snippets(search_text)
        else:
            snippets = list(snippets)
            
        # 应用分类过滤
        if category_filter != "所有分类":
            snippets = [s for s in snippets if s.category == category_filter]
            
        # 按使用次数排序
        snippets.sort(key=lambda x: x.usage_count, reverse=True)
        
        # 添加到列表
        for snippet in snippets:
            item = QListWidgetItem()
            item.setText(f"{snippet.name}")
            item.setToolTip(f"{snippet.description}\n分类: {snippet.category}\n使用次数: {snippet.usage_count}")
            item.setData(Qt.UserRole, snippet.name)
            self.snippet_list.addItem(item)
            
    def _on_selection_changed(self):
        """选择改变时的处理"""
        current_item = self.snippet_list.currentItem()
        has_selection = current_item is not None
        
        self.edit_btn.setEnabled(has_selection)
        self.delete_btn.setEnabled(has_selection)
        self.insert_btn.setEnabled(has_selection)
        
        if has_selection:
            snippet_name = current_item.data(Qt.UserRole)
            snippet = self.storage.get_snippet(snippet_name)
            if snippet:
                self._show_snippet_preview(snippet)
        else:
            self.info_label.setText("选择一个代码片段查看详情")
            self.preview_text.clear()
            
    def _show_snippet_preview(self, snippet: CodeSnippet):
        """显示片段预览"""
        info_text = f"""
<b>名称:</b> {snippet.name}<br>
<b>描述:</b> {snippet.description}<br>
<b>分类:</b> {snippet.category}<br>
<b>快捷键:</b> {snippet.shortcut or '无'}<br>
<b>关键字:</b> {', '.join(snippet.keywords) if snippet.keywords else '无'}<br>
<b>使用次数:</b> {snippet.usage_count}
        """.strip()
        
        self.info_label.setText(info_text)
        self.preview_text.setPlainText(snippet.content)
        
    def _on_item_double_clicked(self, item):
        """双击项目时插入片段"""
        self._insert_snippet()
        
    def _add_snippet(self):
        """添加新片段"""
        editor = SnippetEditor(parent=self)
        if editor.exec() == QDialog.Accepted:
            snippet = editor.get_snippet()
            if snippet:
                if self.storage.add_snippet(snippet):
                    self._load_snippets()
                    QMessageBox.information(self, "成功", "代码片段已添加")
                else:
                    QMessageBox.warning(self, "错误", "添加代码片段失败")
                    
    def _edit_snippet(self):
        """编辑选中片段"""
        current_item = self.snippet_list.currentItem()
        if not current_item:
            return
            
        snippet_name = current_item.data(Qt.UserRole)
        snippet = self.storage.get_snippet(snippet_name)
        if not snippet:
            return
            
        editor = SnippetEditor(snippet, parent=self)
        if editor.exec() == QDialog.Accepted:
            new_snippet = editor.get_snippet()
            if new_snippet:
                # 如果名称改变了，删除旧的
                if new_snippet.name != snippet.name:
                    self.storage.remove_snippet(snippet.name)
                    
                if self.storage.add_snippet(new_snippet):
                    self._load_snippets()
                    QMessageBox.information(self, "成功", "代码片段已更新")
                else:
                    QMessageBox.warning(self, "错误", "更新代码片段失败")
                    
    def _delete_snippet(self):
        """删除选中片段"""
        current_item = self.snippet_list.currentItem()
        if not current_item:
            return
            
        snippet_name = current_item.data(Qt.UserRole)
        
        reply = QMessageBox.question(
            self, "确认删除",
            f"确定要删除代码片段 '{snippet_name}' 吗？",
            QMessageBox.Yes | QMessageBox.No
        )
        
        if reply == QMessageBox.Yes:
            if self.storage.remove_snippet(snippet_name):
                self._load_snippets()
                QMessageBox.information(self, "成功", "代码片段已删除")
            else:
                QMessageBox.warning(self, "错误", "删除代码片段失败")
                
    def _insert_snippet(self):
        """插入选中片段"""
        current_item = self.snippet_list.currentItem()
        if not current_item:
            return
            
        snippet_name = current_item.data(Qt.UserRole)
        snippet = self.storage.get_snippet(snippet_name)
        if snippet:
            # 增加使用次数
            self.storage.increment_usage(snippet_name)
            
            # 发出信号
            self.snippet_selected.emit(snippet.content)
            
    def get_snippet_by_shortcut(self, shortcut: str) -> Optional[CodeSnippet]:
        """根据快捷键获取片段"""
        for snippet in self.storage.get_all_snippets().values():
            if snippet.shortcut.lower() == shortcut.lower():
                return snippet
        return None 